/*
 * TripPlannerPanel.java
 * 
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.apps.admin.gui;

import java.awt.Color;
import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.fpdev.core.algo.path.Pathfinder;
import org.fpdev.apps.admin.AdminClient;

import java.awt.event.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.fpdev.core.basenet.Location;
import org.fpdev.core.basenet.BNode;
import org.fpdev.core.trip.Trip;
import org.fpdev.apps.admin.analysis.AnalysisOps;
import org.fpdev.apps.admin.gui.map.MapDrawItems;
import org.fpdev.core.transit.CalendarService;
import org.fpdev.util.gui.GUIFactory;

public class TripPlannerPanel extends JPanel implements ActionListener, ChangeListener, FocusListener, TimeSelector.TimeSelectorChangeListener {

  private AdminClient ac_;

  DateFormat dateFormat_;

  private JTextField startField_,  endField_,  walkSpeedField_, dateField_;
  private JComboBox tripType_,  calService_,  recent_,  timeMode_,  walkRadius_,  optMode_;
  private List<CalendarService> servicesList_;
  private JRadioButton serviceRBtn_, dateRBtn_;
  private TimeSelector timeSel_;
  private JSlider timeSlider_;
  private JButton runBtn_,  showBtn_,  zoomPathBtn_;
  private JButton zoomFromBtn_,  zoomToBtn_;
  private JButton setFromBtn_,  setToBtn_;
  private JCheckBox showResDialog_;
  private JPanel timeSliderRow_;
  private BikeOptionsPanel bikePanel_;
  private int curTime_; // in minutes since midnight


  private final Color OK_COLOR = new Color(127, 255, 127);
  private final Color ERR_COLOR = new Color(255, 127, 127);

  public TripPlannerPanel(AdminClient ac) {
    ac_ = ac;
    dateFormat_ = new SimpleDateFormat("M/d/yy");

    setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    setBorder(new EmptyBorder(6, 6, 6, 6));
    Calendar cal = Calendar.getInstance();

    JPanel tripTypeRow = new JPanel();
    tripTypeRow.setLayout(new BoxLayout(tripTypeRow, BoxLayout.X_AXIS));
    tripTypeRow.add(GUIFactory.newLabel("Trip type: "));
    tripType_ = GUIFactory.newComboBox(120);
    tripType_.addItem("Walk to Transit");
    tripType_.addItem("Bike to Transit");
    tripType_.addItem("Walk Only");
    tripType_.addItem("Bike Only");
    tripType_.setSelectedIndex(1);
    tripTypeRow.add(tripType_);
    tripTypeRow.setAlignmentX(LEFT_ALIGNMENT);

    startField_ = GUIFactory.newTextField("", 50, 0);
    startField_.addActionListener(this);
    startField_.setAlignmentX(LEFT_ALIGNMENT);
    startField_.addFocusListener(this);

    endField_ = GUIFactory.newTextField("", 50, 0);
    endField_.addActionListener(this);
    endField_.setAlignmentX(LEFT_ALIGNMENT);
    endField_.addFocusListener(this);

    recent_ = GUIFactory.newComboBox(0);
    recent_.addActionListener(this);
    recent_.setAlignmentX(LEFT_ALIGNMENT);
    refreshRecentQueries();

    JPanel optModeRow = new JPanel();
    optModeRow.setLayout(new BoxLayout(optModeRow, BoxLayout.X_AXIS));
    optModeRow.add(GUIFactory.newLabel("Optimize for: "));
    optMode_ = GUIFactory.newComboBox(120);
    optMode_.addItem("Travel Time");
    optMode_.addItem("No. of Transfers");
    optModeRow.add(optMode_);
    optModeRow.setAlignmentX(LEFT_ALIGNMENT);

    // time selector row

    curTime_ = (Math.max(0, cal.get(Calendar.HOUR_OF_DAY) - 1)) * 60 + cal.get(Calendar.MINUTE);

    timeMode_ = GUIFactory.newComboBox(75);
    timeMode_.addItem("Depart at");
    timeMode_.addItem("Arrive by");


    timeSel_ = new TimeSelector(this);

    JPanel timeSelRow = new JPanel();
    timeSelRow.setLayout(new BoxLayout(timeSelRow, BoxLayout.X_AXIS));
    timeSelRow.add(timeMode_);
    timeSelRow.add(Box.createHorizontalStrut(4));
    timeSelRow.add(timeSel_);
    timeSelRow.setAlignmentX(LEFT_ALIGNMENT);

    // time slider row
    timeSlider_ = new JSlider(-60, 60, 0);
    //timeSlider_.setPaintTrack(false);
    timeSlider_.addChangeListener(this);
    //GUIFactory.setSize(timeSlider_, 60, 20);
    timeSliderRow_ = new JPanel();
    timeSliderRow_.setLayout(new BoxLayout(timeSliderRow_, BoxLayout.X_AXIS));
    timeSliderRow_.add(GUIFactory.newLabel("Adjust Time: "));
    timeSliderRow_.add(timeSlider_);
    timeSliderRow_.setAlignmentX(LEFT_ALIGNMENT);

    // service/date selection rows
    ButtonGroup rBtnGroup = new ButtonGroup();
    serviceRBtn_ = GUIFactory.newRadioButton("Service: ", rBtnGroup, this);
    dateRBtn_ = GUIFactory.newRadioButton("Date: ", rBtnGroup, this);
    serviceRBtn_.setSelected(true);

    servicesList_ = new ArrayList<CalendarService>();
    calService_ = GUIFactory.newComboBox(100); //new JComboBox();

    for(CalendarService service : ac_.getEngine().getRoutes().getCalendarServices().getServices()) {
      calService_.addItem(service.getName());
      servicesList_.add(service);
    }
    dateField_ = GUIFactory.newTextField(dateFormat_.format(Calendar.getInstance().getTime()), 0, 80);

    JPanel serviceSelRow = GUIFactory.newRowPanel();
    serviceSelRow.add(serviceRBtn_);
    serviceSelRow.add(calService_);
    serviceSelRow.setAlignmentX(LEFT_ALIGNMENT);

    JPanel dateSelRow = GUIFactory.newRowPanel();
    dateSelRow.add(dateRBtn_);
    dateSelRow.add(dateField_);
    dateSelRow.setAlignmentX(LEFT_ALIGNMENT);
    //serviceSelRow.add(_);


    walkSpeedField_ = GUIFactory.newTextField("2.5", 5, 30); //new JTextField("2.5", 5);
    walkRadius_ = GUIFactory.newComboBox(50);
    walkRadius_.addItem(".1");
    walkRadius_.addItem(".25");
    walkRadius_.addItem(".5");
    walkRadius_.addItem(".75");
    walkRadius_.addItem("1.0");
    walkRadius_.addItem("1.5");
    walkRadius_.addItem("2.0");
    walkRadius_.setSelectedIndex(3);

    JPanel walkSpeedRow = new JPanel();
    walkSpeedRow.setLayout(new BoxLayout(walkSpeedRow, BoxLayout.X_AXIS));
    walkSpeedRow.add(GUIFactory.newLabel("Walk Speed: "));
    walkSpeedRow.add(walkSpeedField_);
    walkSpeedRow.add(Box.createHorizontalStrut(5));
    walkSpeedRow.add(GUIFactory.newLabel("Radius: "));
    walkSpeedRow.add(walkRadius_);
    walkSpeedRow.setAlignmentX(LEFT_ALIGNMENT);

    bikePanel_ = new BikeOptionsPanel();
    bikePanel_.setAlignmentX(LEFT_ALIGNMENT);

    runBtn_ = GUIFactory.newButton("Find Path", 60); // new JButton("Recalc");
    runBtn_.addActionListener(this);
    //showBtn_ = new JButton("Details");
    //showBtn_.addActionListener(this);
    zoomPathBtn_ = GUIFactory.newButton("All", 40, this);
    zoomFromBtn_ = GUIFactory.newButton("Start", 40, this);
    zoomToBtn_ = GUIFactory.newButton("End", 40, this);

    JPanel btnRow = new JPanel();
    btnRow.setLayout(new BoxLayout(btnRow, BoxLayout.X_AXIS));
    btnRow.add(Box.createHorizontalGlue());
    btnRow.add(runBtn_);
    //btnRow.add(showBtn_);
    btnRow.add(Box.createHorizontalStrut(5));
    btnRow.add(GUIFactory.newLabel("Zoom:"));
    btnRow.add(Box.createHorizontalStrut(5));
    btnRow.add(zoomPathBtn_);
    //btnRow.add(Box.createHorizontalStrut(5));
    btnRow.add(zoomFromBtn_);
    //btnRow.add(Box.createHorizontalStrut(5));
    btnRow.add(zoomToBtn_);
    btnRow.add(Box.createHorizontalGlue());
    btnRow.setAlignmentX(LEFT_ALIGNMENT);

    showResDialog_ = new JCheckBox("Show Details by Default", true);
    showResDialog_.setAlignmentX(LEFT_ALIGNMENT);

    add(tripTypeRow);
    add(Box.createVerticalStrut(5));
    add(GUIFactory.newLabel("Start location:"));
    add(startField_);
    add(Box.createVerticalStrut(4));
    add(GUIFactory.newLabel("End location:"));
    add(endField_);
    add(Box.createVerticalStrut(4));
    add(recent_);
    add(Box.createVerticalStrut(10));
    add(optModeRow);
    add(Box.createVerticalStrut(5));
    add(timeSelRow);
    add(Box.createVerticalStrut(5));
    add(timeSliderRow_);
    add(Box.createVerticalStrut(5));
    add(serviceSelRow);
    add(Box.createVerticalStrut(5));
    add(dateSelRow);
    add(Box.createVerticalStrut(5));
    add(walkSpeedRow);
    add(Box.createVerticalStrut(8));
    add(bikePanel_);
    add(Box.createVerticalStrut(8));
    add(btnRow);
    add(Box.createVerticalGlue());
  }

  @Override
  public void actionPerformed(ActionEvent e) {

    if (e.getSource() == startField_) {
      newStartLocation();
    }

    if (e.getSource() == endField_) {
      newEndLocation();
    }

    if (e.getSource() == runBtn_) {
      Trip trip = ac_.getAnalysisOps().findBestTrip();
    //cv_.getGUI().zoomToTrip(trip);
    //cv_.getGUI().getMapPanel().updated(false, true);
    //cv_.getGUI().getMapPanel().repaint();
    }

    if (e.getSource() == zoomPathBtn_) {
      ac_.getGUI().getMapPanel().zoomDrawable(MapDrawItems.BEST_TRIP, 0.1);
    }
    int d = 2500;
    if (e.getSource() == zoomFromBtn_) {
      if (ac_.getAnalysisOps().getEndLocation() == null) {
        return;
      }
      BNode node = ac_.getAnalysisOps().getStartLocation().getNode();
      ac_.getGUI().getMapPanel().zoomRange(node.getX() - d, node.getY() - d, node.getX() + d, node.getY() + d);
      ac_.getGUI().getMapPanel().refresh(true, true, true);
    }
    if (e.getSource() == zoomToBtn_) {
      if (ac_.getAnalysisOps().getEndLocation() == null) {
        return;
      }
      BNode node = ac_.getAnalysisOps().getEndLocation().getNode();
      ac_.getGUI().getMapPanel().zoomRange(node.getX() - d, node.getY() - d, node.getX() + d, node.getY() + d);
      ac_.getGUI().getMapPanel().refresh(true, true, true);
    }

  }

  @Override
  public void focusGained(FocusEvent e) {
    if (e.getSource() == startField_) {
      startField_.setSelectionStart(0);
      startField_.setSelectionEnd(startField_.getText().length());
    }
    if (e.getSource() == endField_) {
      endField_.setSelectionStart(0);
      endField_.setSelectionEnd(endField_.getText().length());
    }
  }

  @Override
  public void focusLost(FocusEvent e) {
  }

  @Override
  public void timeSelectorChanged() {
    curTime_ = timeSel_.getMTime();
  }

  public void refreshRecentQueries() {
    recent_.removeAllItems();
    recent_.addItem("Recent Searches:");
    int i = 1;
    Iterator<String> descriptions = ac_.getAnalysisOps().getRecentTripQueries().getDescriptions();
    while (descriptions.hasNext()) {
      recent_.addItem(i + ". " + descriptions.next());
      i++;
    }
    recent_.addItem("[Clear Recent Search List]");
  }

  /*private void adjustTime(int field, int value) {
  if(field == FIELD_HOUR) hourField_.setText(""+value);
  if(field == FIELD_MINUTE) minField_.setText(""+value);
  updateCurrentTime();
  }
  private void updateCurrentTime() {
  int hour = new Integer(hourField_.getText()).intValue();
  int min = new Integer(minField_.getText()).intValue();
  if(hour == 12 && ampm_.getSelectedIndex()==0) hour = 0; // 12 midnight
  else if(hour != 12 && ampm_.getSelectedIndex() == 1) hour+=12; // 1pm to 11pm
  curTime_ = hour*60 + min;    
  }*/
  public void checkStartLocation(Location start) {
    if (start == null || !start.getDispText(false).equals(startField_.getText())) {
      newStartLocation();
    }
  }

  public void checkEndLocation(Location end) {
    if (end == null || !end.getDispText(false).equals(endField_.getText())) {
      newEndLocation();
    }
  }

  private void newStartLocation() {
    int status = ac_.getAnalysisOps().resolveStart(startField_.getText());
    if (status == AnalysisOps.RESOLVE_SUCCESS) {
      startField_.setBackground(OK_COLOR);
      startField_.setText(ac_.getAnalysisOps().getStartLocation().getDispText(false));
    } else if (status == AnalysisOps.RESOLVE_FAILURE) {
      startField_.setBackground(ERR_COLOR);
      ac_.msg("Error resolving start location input");
    } else if (startField_.getText().length() == 0) {
      startField_.setBackground(Color.white);
    }
  }

  private void newEndLocation() {
    int status = ac_.getAnalysisOps().resolveEnd(endField_.getText());
    if (status == AnalysisOps.RESOLVE_SUCCESS) {
      endField_.setBackground(OK_COLOR);
      endField_.setText(ac_.getAnalysisOps().getEndLocation().getDispText(false));
    } else if (status == AnalysisOps.RESOLVE_FAILURE) {
      endField_.setBackground(ERR_COLOR);
      ac_.msg("Error resolving end location input");
    } else if (endField_.getText().length() == 0) {
      endField_.setBackground(Color.white);
    }
  }

  public void stateChanged(ChangeEvent e) {
    timeSliderChanged(e);
  }

  public int getTripType() {
    return tripType_.getSelectedIndex() + 1;
  }

  public Pathfinder.TimeMode getTimeMode() {
    return timeMode_.getSelectedIndex() == 0 ? Pathfinder.TimeMode.DEPART_AT : Pathfinder.TimeMode.ARRIVE_BY;
  }

  public int getOptMode() {
    return optMode_.getSelectedIndex() == 0 ? Pathfinder.TRAVEL_TIME : Pathfinder.NUM_TRANSFERS;
  }

  /*public int getSTime() {
  return CUtil.getSTime(new Integer(hourField_.getText()).intValue(), new Integer(minField_.getText()).intValue(), ampm_.getSelectedIndex()+1);
  }*/
  public int getSTime() {
    return timeSel_.getSTime();
  }

  /*public Timetable.DayOfWeek getDayOfWeek() {
    return Timetable.toDayOfWeek(calService_.getSelectedIndex() + 1);
  }*/

  public CalendarService getService() {
    if(serviceRBtn_.isSelected()) {
      System.out.println("service selected");
      return servicesList_.get(calService_.getSelectedIndex());
    }
    else {
      Date date;
      try {
        date = dateFormat_.parse(dateField_.getText());
      } catch (ParseException ex) {
        ac_.msg("Error: improperly formatted date");
        return null;
      }
      return ac_.getEngine().getRoutes().getCalendarServices().resolveDate(date);
    }
  }

  public double getWalkSpeedFPS() {
    double mph = new Double(walkSpeedField_.getText()).doubleValue();
    //System.out.println("mph "+mph);
    double fps = mph * (22.0 / 15.0);
    //System.out.println("fps "+fps);
    return fps; //(new Double(walkSpeed_.getText()).doubleValue())*(22/15);
  }

  public double getBikeTypeFactor() {
    return bikePanel_.getType(); //new Double(bikeTypeFactField_.getText()).doubleValue();
  }

  public double getBikeTopFactor() {
    return bikePanel_.getTopo(); //new Double(bikeTopFactField_.getText()).doubleValue();
  }

  public double getMaxWalkDistFt() {
    double mi = new Double((String) walkRadius_.getSelectedItem()).doubleValue();
    return mi * 5280.0;
  }

  public boolean getShowResults() {
    return showResDialog_.isSelected();
  }

  public void setStartText(String text) {
    startField_.setText(text);
    startField_.setBackground(OK_COLOR);
  }

  public void setEndText(String text) {
    endField_.setText(text);
    endField_.setBackground(OK_COLOR);
  }

  public void clearStartEndText() {
    startField_.setText("");
    startField_.setBackground(Color.white);
    endField_.setText("");
    endField_.setBackground(Color.white);
  }

  //private int newTime_;
  public void timeSliderChanged(ChangeEvent e) {
    //System.out.println("slider changed");
    if (timeSlider_.getValueIsAdjusting()) {
      timeSel_.updateTime(curTime_ + timeSlider_.getValue());
    } else {
      curTime_ = curTime_ + timeSlider_.getValue();
      timeSel_.updateTime(curTime_);
      timeSliderRow_.remove(timeSlider_);
      timeSlider_ = new JSlider(-60, 60, 0);
      timeSlider_.addChangeListener(this);
      timeSliderRow_.add(timeSlider_);
      timeSliderRow_.revalidate();
    }

  }
}
































